/* * The MIT License * * Copyright (c) 2010-2017, Manufacture Française des Pneumatiques Michelin, * Thomas Maurel, Romain Seguy, Synopsys Inc., Oleg Nenashev and contributors * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package com.michelin.cio.hudson.plugins.rolestrategy; import com.synopsys.arc.jenkins.plugins.rolestrategy.RoleType; import com.synopsys.arc.jenkins.plugins.rolestrategy.UserMacroExtension; import com.thoughtworks.xstream.converters.Converter; import com.thoughtworks.xstream.converters.MarshallingContext; import com.thoughtworks.xstream.converters.UnmarshallingContext; import com.thoughtworks.xstream.io.HierarchicalStreamReader; import com.thoughtworks.xstream.io.HierarchicalStreamWriter; import hudson.Extension; import hudson.model.AbstractItem; import hudson.model.Computer; import hudson.model.Hudson; import hudson.model.Item; import hudson.model.Job; import hudson.model.Project; import hudson.model.Run; import hudson.model.View; import hudson.scm.SCM; import hudson.security.ACL; import hudson.security.AccessControlled; import hudson.security.AuthorizationStrategy; import hudson.security.GlobalMatrixAuthorizationStrategy; import hudson.security.Permission; import hudson.security.PermissionGroup; import hudson.security.SidACL; import java.io.IOException; import java.io.UnsupportedEncodingException; import javax.servlet.ServletException; import hudson.util.VersionNumber; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.SortedMap; import java.util.logging.Level; import java.util.logging.Logger; import javax.annotation.CheckForNull; import jenkins.model.Jenkins; import net.sf.json.JSONObject; import org.acegisecurity.acls.sid.PrincipalSid; import org.jenkinsci.plugins.rolestrategy.permissions.DangerousPermissionHandlingMode; import org.jenkinsci.plugins.rolestrategy.permissions.DangerousPermissionHelper; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; import org.kohsuke.stapler.QueryParameter; import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.StaplerResponse; import org.kohsuke.stapler.interceptor.RequirePOST; /** * Role-based authorization strategy. * @author Thomas Maurel */ public class RoleBasedAuthorizationStrategy extends AuthorizationStrategy { public final static String GLOBAL = "globalRoles"; public final static String PROJECT = "projectRoles"; public final static String SLAVE = "slaveRoles"; public final static String MACRO_ROLE = "roleMacros"; public final static String MACRO_USER = "userMacros"; private static final Logger LOGGER = Logger.getLogger(RoleBasedAuthorizationStrategy.class.getName()); /** {@link RoleMap}s associated to each {@link AccessControlled} class */ private final Map <String, RoleMap> grantedRoles = new HashMap < String, RoleMap >(); /** * Get the root ACL. * @return The global ACL */ @Override public SidACL getRootACL() { RoleMap root = getRoleMap(GLOBAL); return root.getACL(RoleType.Global, null); } /** * Universal function for getting ACL for different * @param roleMapName Name of the role map section * @param itemName Name of the item for patterns * @return ACL */ private ACL getACL(String roleMapName, String itemName, RoleType roleType, AccessControlled item) { SidACL acl; RoleMap roleMap = grantedRoles.get(roleMapName); if (roleMap == null) { acl = getRootACL(); } else { // Create a sub-RoleMap matching the project name, and create an inheriting from root ACL acl = roleMap.newMatchingRoleMap(itemName).getACL(roleType, item).newInheritingACL(getRootACL()); } return acl; } /** * Get the specific ACL for projects. * @param project The access-controlled project * @return The project specific ACL */ @Override public ACL getACL(Job<?,?> project) { return getACL((AbstractItem) project); } @Override public ACL getACL(AbstractItem project) { return getACL(PROJECT, project.getFullName(), RoleType.Project, project); } @Override public ACL getACL(Computer computer) { return getACL(SLAVE, computer.getName(), RoleType.Slave, computer); } /** * Used by the container realm. * @return All the sids referenced by the strategy */ @Override public Collection<String> getGroups() { Set<String> sids = new HashSet<String>(); for (Map.Entry entry : this.grantedRoles.entrySet()) { RoleMap roleMap = (RoleMap) entry.getValue(); sids.addAll(roleMap.getSids(true)); } return sids; } /** * Get the roles from the global {@link RoleMap}. * <p>The returned sorted map is unmodifiable.</p> * @param type The object type controlled by the {@link RoleMap} * @return All roles from the global {@link RoleMap} */ public SortedMap<Role, Set<String>> getGrantedRoles(String type) { RoleMap roleMap = this.getRoleMap(type); if (roleMap != null) { return roleMap.getGrantedRoles(); } return null; } /** * Get all the SIDs referenced by specified {@link RoleMap} type. * @param type The object type controlled by the {@link RoleMap} * @return All SIDs from the specified {@link RoleMap}. */ public Set<String> getSIDs(String type) { RoleMap roleMap = this.getRoleMap(type); if (roleMap != null) { return roleMap.getSids(); } return null; } /** * Get the {@link RoleMap} associated to the given class. * @param type The object type controlled by the {@link RoleMap} * @return The associated {@link RoleMap} */ private RoleMap getRoleMap(String type) { RoleMap map; if (grantedRoles.containsKey(type)) { map = grantedRoles.get(type); } else { // Create it if it doesn't exist map = new RoleMap(); grantedRoles.put(type, map); } return map; } /** * Returns a map associating a string representation with each {@link RoleMap}. * <p>This method is intended to be used for XML serialization purposes (take * a look at the {@link ConverterImpl}) and, as such, must remain private * since it exposes all the security config.</p> */ private Map<String, RoleMap> getRoleMaps() { return grantedRoles; } /** * Add the given {@link Role} to the {@link RoleMap} associated to the provided class. * @param type The {@link AccessControlled} class referencing the {@link RoleMap} * @param role The {@link Role} to add */ private void addRole(String type, Role role) { RoleMap roleMap = this.grantedRoles.get(type); if (roleMap != null) { roleMap.addRole(role); } else { // Create the RoleMap if it doesnt exist roleMap = new RoleMap(); roleMap.addRole(role); grantedRoles.put(type, roleMap); } } /** * Assign a role to a sid * @param type The type of role * @param role The role to assign * @param sid The sid to assign to */ private void assignRole(String type, Role role, String sid) { RoleMap roleMap = this.grantedRoles.get(type); if (roleMap != null && roleMap.hasRole(role)) { roleMap.assignRole(role, sid); } } /** * API method to add roles * <p> * example: curl -X POST localhost:8080/role-strategy/strategy/addRole --data "type=globalRoles&roleName=ADM& * permissionIds=hudson.model.Item.Discover,hudson.model.Item.ExtendedRead&overwrite=true" * * @param type (globalRoles, projectRoles) * @param roleName name of role * @param permissionIds comma separated list of IDs for given roleName * @param overwrite overwrite existing role * @throws IOException in case saving changes fails * @since 2.4.1 */ @RequirePOST @Restricted(NoExternalUse.class) public void doAddRole(@QueryParameter(required = true) String type, @QueryParameter(required = true) String roleName, @QueryParameter(required = true) String permissionIds, @QueryParameter(required = true) String overwrite, @QueryParameter(required = false) String pattern) throws IOException { Jenkins.getInstance().checkPermission(Jenkins.ADMINISTER); boolean overwriteb = Boolean.parseBoolean(overwrite); String pttrn = ".*"; if (!type.equals(RoleBasedAuthorizationStrategy.GLOBAL) && pattern != null) { pttrn = pattern; } ArrayList<String> permissionList = new ArrayList<String>(); permissionList.addAll(Arrays.asList(permissionIds.split(","))); Set<Permission> permissionSet = new HashSet<Permission>(); for (String p : permissionList) { permissionSet.add(Permission.fromId(p)); } Role role = new Role(roleName, pttrn, permissionSet); if (overwriteb) { Role role2 = this.grantedRoles.get(type).getRole(roleName); if (role2 != null) { this.grantedRoles.get(type).removeRole(role2); } } addRole(type, role); persistChanges(); } /** * API method to remove roles * <p> * example: curl -X POST localhost:8080/role-strategy/strategy/removeRoles --data "type=globalRoles& * roleNames=ADM,DEV" * * @param type (globalRoles, projectRoles) * @param roleNames comma separated list of roles to remove from type * @throws IOException in case saving changes fails * @since 2.4.1 */ @RequirePOST @Restricted(NoExternalUse.class) public void doRemoveRoles(@QueryParameter(required = true) String type, @QueryParameter(required = true) String roleNames) throws IOException { checkAdminPerm(); RoleMap roleMap = this.grantedRoles.get(type); if (roleMap != null) { String[] split = roleNames.split(","); for (String aSplit : split) { Role role = roleMap.getRole(aSplit); if (role != null) { roleMap.removeRole(role); } } } persistChanges(); } /** * API method to assign SID to role * <p> * example: curl -X POST localhost:8080/role-strategy/strategy/assignRole --data "type=globalRoles&roleName=ADM * &sid=username" * * @param type (globalRoles, projectRoles) * @param roleName name of role (single, no list) * @param sid user ID (single, no list) * @throws IOException in case saving changes fails * @since 2.4.1 */ @RequirePOST @Restricted(NoExternalUse.class) public void doAssignRole(@QueryParameter(required = true) String type, @QueryParameter(required = true) String roleName, @QueryParameter(required = true) String sid) throws IOException { checkAdminPerm(); RoleMap roleMap = this.grantedRoles.get(type); if (roleMap != null) { Role role = roleMap.getRole(roleName); if (role != null) { assignRole(type, role, sid); } persistChanges(); } } private static void persistChanges() throws IOException { instance().save(); } private static Jenkins instance() { return Jenkins.getInstance(); } private static void checkAdminPerm() { instance().checkPermission(Jenkins.ADMINISTER); } /** * API method to delete a SID * <p> * example: curl -X POST localhost:8080/role-strategy/strategy/deleteSid --data "type=globalRoles&sid=username" * * @param type (globalRoles, projectRoles) * @param sid user ID to remove * @throws IOException in case saving changes fails * @since 2.4.1 */ @RequirePOST @Restricted(NoExternalUse.class) public void doDeleteSid(@QueryParameter(required = true) String type, @QueryParameter(required = true) String sid) throws IOException { checkAdminPerm(); RoleMap roleMap = this.grantedRoles.get(type); if (roleMap != null) { roleMap.deleteSids(sid); } persistChanges(); } @Extension public static final DescriptorImpl DESCRIPTOR = new DescriptorImpl(); /** * Converter used to persist and retrieve the strategy from disk. * * <p>This converter is there to manually handle the marshalling/unmarshalling * of this strategy: Doing so is a little bit dirty but allows to easily update * the plugin when new access controlled object (for the moment: Job and * Project) will be introduced. If it's the case, there's only the need to * update the getRoleMaps() method.</p> */ public static class ConverterImpl implements Converter { public boolean canConvert(Class type) { return type==RoleBasedAuthorizationStrategy.class; } public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) { RoleBasedAuthorizationStrategy strategy = (RoleBasedAuthorizationStrategy)source; // Role maps Map<String, RoleMap> maps = strategy.getRoleMaps(); for (Map.Entry<String, RoleMap> map : maps.entrySet()) { RoleMap roleMap = map.getValue(); writer.startNode("roleMap"); writer.addAttribute("type", map.getKey()); for (Map.Entry<Role, Set<String>> grantedRole : roleMap.getGrantedRoles().entrySet()) { Role role = grantedRole.getKey(); if (role != null) { writer.startNode("role"); writer.addAttribute("name", role.getName()); writer.addAttribute("pattern", role.getPattern().pattern()); writer.startNode("permissions"); for (Permission permission : role.getPermissions()) { writer.startNode("permission"); writer.setValue(permission.getId()); writer.endNode(); } writer.endNode(); writer.startNode("assignedSIDs"); for (String sid : grantedRole.getValue()) { writer.startNode("sid"); writer.setValue(sid); writer.endNode(); } writer.endNode(); writer.endNode(); } } writer.endNode(); } } public Object unmarshal(HierarchicalStreamReader reader, final UnmarshallingContext context) { final RoleBasedAuthorizationStrategy strategy = create(); boolean showDangerousPermissionsDefined = false; while(reader.hasMoreChildren()) { reader.moveDown(); // roleMaps if(reader.getNodeName().equals("roleMap")) { String type = reader.getAttribute("type"); RoleMap map = new RoleMap(); while(reader.hasMoreChildren()) { reader.moveDown(); String name = reader.getAttribute("name"); String pattern = reader.getAttribute("pattern"); Set<Permission> permissions = new HashSet<Permission>(); String next = reader.peekNextChild(); if (next != null && next.equals("permissions")) { reader.moveDown(); while(reader.hasMoreChildren()) { reader.moveDown(); Permission p = Permission.fromId(reader.getValue()); if (p != null) { permissions.add(p); } reader.moveUp(); } reader.moveUp(); } Role role = new Role(name, pattern, permissions); map.addRole(role); next = reader.peekNextChild(); if (next != null && next.equals("assignedSIDs")) { reader.moveDown(); while(reader.hasMoreChildren()) { reader.moveDown(); map.assignRole(role, reader.getValue()); reader.moveUp(); } reader.moveUp(); } reader.moveUp(); } strategy.grantedRoles.put(type, map); } reader.moveUp(); } return strategy; } protected RoleBasedAuthorizationStrategy create() { return new RoleBasedAuthorizationStrategy(); } } /** * Retrieves instance of the strategy. * @return Strategy instance or {@code null} if it is disabled. */ @CheckForNull public static RoleBasedAuthorizationStrategy getInstance() { final Jenkins jenkins = Jenkins.getInstance(); final AuthorizationStrategy authStrategy= jenkins != null ? jenkins.getAuthorizationStrategy() : null; if (authStrategy instanceof RoleBasedAuthorizationStrategy) { return (RoleBasedAuthorizationStrategy)authStrategy; } // Nothing to do here, not a Role strategy return null; } /** * Updates macro roles * @since 2.1.0 */ void renewMacroRoles() { //TODO: add mandatory roles // Check role extensions for (UserMacroExtension userExt : UserMacroExtension.all()) { if (userExt.IsApplicable(RoleType.Global)) { getRoleMap(GLOBAL).getSids().contains(userExt.getName()); } } } /** * Control job create using {@link org.jenkinsci.plugins.rolestrategy.RoleBasedProjectNamingStrategy}. * @since 2.2.0 */ public static boolean isCreateAllowed(){ return Jenkins.getVersion().isNewerThan(new VersionNumber("1.566")); } /** * Descriptor used to bind the strategy to the Web forms. */ public static final class DescriptorImpl extends GlobalMatrixAuthorizationStrategy.DescriptorImpl { @Override public String getDisplayName() { return Messages.RoleBasedAuthorizationStrategy_DisplayName(); } /** * Called on role management form's submission. */ public void doRolesSubmit(StaplerRequest req, StaplerResponse rsp) throws UnsupportedEncodingException, ServletException, FormException, IOException { checkAdminPerm(); req.setCharacterEncoding("UTF-8"); JSONObject json = req.getSubmittedForm(); AuthorizationStrategy strategy = this.newInstance(req, json); instance().setAuthorizationStrategy(strategy); // Persist the data persistChanges(); } /** * Called on role assignment form's submission. */ public void doAssignSubmit(StaplerRequest req, StaplerResponse rsp) throws UnsupportedEncodingException, ServletException, FormException, IOException { checkAdminPerm(); req.setCharacterEncoding("UTF-8"); JSONObject json = req.getSubmittedForm(); AuthorizationStrategy oldStrategy = instance().getAuthorizationStrategy(); if (json.has(GLOBAL) && json.has(PROJECT) && oldStrategy instanceof RoleBasedAuthorizationStrategy) { RoleBasedAuthorizationStrategy strategy = (RoleBasedAuthorizationStrategy) oldStrategy; Map<String, RoleMap> maps = strategy.getRoleMaps(); for (Map.Entry<String, RoleMap> map : maps.entrySet()) { // Get roles and skip non-existent role entries (backward-comp) RoleMap roleMap = map.getValue(); roleMap.clearSids(); JSONObject roles = json.getJSONObject(map.getKey()); if (roles.isNullObject()) { continue; } for (Map.Entry<String,JSONObject> r : (Set<Map.Entry<String,JSONObject>>)roles.getJSONObject("data").entrySet()) { String sid = r.getKey(); for (Map.Entry<String,Boolean> e : (Set<Map.Entry<String,Boolean>>)r.getValue().entrySet()) { if (e.getValue()) { Role role = roleMap.getRole(e.getKey()); if (role != null && sid != null && !sid.equals("")) { roleMap.assignRole(role, sid); } } } } } // Persist the data persistChanges(); } } /** * Method called on Hudson Manage panel submission, and plugin specific forms * to create the {@link AuthorizationStrategy} object. */ @Override public AuthorizationStrategy newInstance(StaplerRequest req, JSONObject formData) throws FormException { AuthorizationStrategy oldStrategy = instance().getAuthorizationStrategy(); RoleBasedAuthorizationStrategy strategy; // If the form contains data, it means the method has been called by plugin // specifics forms, and we need to handle it. if (formData.has(GLOBAL) && formData.has(PROJECT) && formData.has(SLAVE) && oldStrategy instanceof RoleBasedAuthorizationStrategy) { strategy = new RoleBasedAuthorizationStrategy(); JSONObject globalRoles = formData.getJSONObject(GLOBAL); for (Map.Entry<String,JSONObject> r : (Set<Map.Entry<String,JSONObject>>)globalRoles.getJSONObject("data").entrySet()) { String roleName = r.getKey(); Set<Permission> permissions = new HashSet<Permission>(); for (Map.Entry<String,Boolean> e : (Set<Map.Entry<String,Boolean>>)r.getValue().entrySet()) { if (e.getValue()) { Permission p = Permission.fromId(e.getKey()); permissions.add(p); } } Role role = new Role(roleName, permissions); strategy.addRole(GLOBAL, role); RoleMap roleMap = ((RoleBasedAuthorizationStrategy) oldStrategy).getRoleMap(GLOBAL); if (roleMap != null) { Set<String> sids = roleMap.getSidsForRole(roleName); if (sids != null) { for (String sid : sids) { strategy.assignRole(GLOBAL, role, sid); } } } } ReadRoles(formData, PROJECT, strategy, (RoleBasedAuthorizationStrategy)oldStrategy); ReadRoles(formData, SLAVE, strategy, (RoleBasedAuthorizationStrategy)oldStrategy); } // When called from Hudson Manage panel, but was already on a role-based strategy else if (oldStrategy instanceof RoleBasedAuthorizationStrategy) { // Do nothing, keep the same strategy strategy = (RoleBasedAuthorizationStrategy) oldStrategy; } // When called from Hudson Manage panel, but when the previous strategy wasn't // role-based, it means we need to create an admin role, and assign it to the // current user to not throw him out of the webapp else { strategy = new RoleBasedAuthorizationStrategy(); Role adminRole = createAdminRole(); strategy.addRole(GLOBAL, adminRole); strategy.assignRole(GLOBAL, adminRole, getCurrentUser()); } strategy.renewMacroRoles(); return strategy; } private void ReadRoles(JSONObject formData, String roleType, RoleBasedAuthorizationStrategy targetStrategy, RoleBasedAuthorizationStrategy oldStrategy) { if (!formData.has(roleType)) { assert false : "Unexistent Role type " + roleType; return; } JSONObject projectRoles = formData.getJSONObject(roleType); if (!projectRoles.containsKey("data")) { assert false : "No data at role description"; return; } for (Map.Entry<String,JSONObject> r : (Set<Map.Entry<String,JSONObject>>)projectRoles.getJSONObject("data").entrySet()) { String roleName = r.getKey(); Set<Permission> permissions = new HashSet<Permission>(); String pattern = r.getValue().getString("pattern"); if (pattern != null) { r.getValue().remove("pattern"); } else { pattern = ".*"; } for (Map.Entry<String,Boolean> e : (Set<Map.Entry<String,Boolean>>)r.getValue().entrySet()) { if (e.getValue()) { Permission p = Permission.fromId(e.getKey()); permissions.add(p); } } Role role = new Role(roleName, pattern, permissions); targetStrategy.addRole(roleType, role); RoleMap roleMap = oldStrategy.getRoleMap(roleType); if (roleMap != null) { Set<String> sids = roleMap.getSidsForRole(roleName); if (sids != null) { for (String sid : sids) { targetStrategy.assignRole(roleType, role, sid); } } } } } /** * Create an admin role. */ private Role createAdminRole() { Set<Permission> permissions = new HashSet<Permission>(); for (PermissionGroup group : getGroups(GLOBAL)) { for (Permission permission : group) { permissions.add(permission); } } Role role = new Role("admin", permissions); return role; } /** * Get the current user ({@code Anonymous} if not logged-in). * @return Sid of the current user */ private String getCurrentUser() { PrincipalSid currentUser = new PrincipalSid(Hudson.getAuthentication()); return currentUser.getPrincipal(); } /** * Get the needed permissions groups. */ public List<PermissionGroup> getGroups(String type) { List<PermissionGroup> groups; if (type.equals(GLOBAL)) { groups = new ArrayList<PermissionGroup>(PermissionGroup.getAll()); groups.remove(PermissionGroup.get(Permission.class)); } else if (type.equals(PROJECT)) { groups = new ArrayList<PermissionGroup>(PermissionGroup.getAll()); groups.remove(PermissionGroup.get(Permission.class)); groups.remove(PermissionGroup.get(Hudson.class)); groups.remove(PermissionGroup.get(Computer.class)); groups.remove(PermissionGroup.get(View.class)); } else if (type.equals(SLAVE)) { groups = new ArrayList<PermissionGroup>(PermissionGroup.getAll()); groups.remove(PermissionGroup.get(Permission.class)); groups.remove(PermissionGroup.get(Hudson.class)); groups.remove(PermissionGroup.get(View.class)); // Project, SCM and Run permissions groups.remove(PermissionGroup.get(Item.class)); groups.remove(PermissionGroup.get(SCM.class)); groups.remove(PermissionGroup.get(Run.class)); } else { groups = null; } return groups; } @Restricted(NoExternalUse.class) public boolean hasDangerousPermissions() { RoleBasedAuthorizationStrategy instance = RoleBasedAuthorizationStrategy.getInstance(); if (instance == null) { // Should never happen return false; } return DangerousPermissionHelper.hasDangerousPermissions(instance); } @Restricted(NoExternalUse.class) public boolean showPermission(String type, Permission p) { return showPermission(type, p, false); } /** * Check if the permission should be displayed. * For Stapler only. */ @Restricted(NoExternalUse.class) public boolean showPermission(String type, Permission p, boolean showDangerous) { if(type.equals(GLOBAL)) { if (DangerousPermissionHelper.isDangerous(p)) { // Consult with the Security strategy RoleBasedAuthorizationStrategy instance = RoleBasedAuthorizationStrategy.getInstance(); if (instance == null) { // Should never happen return false; } // When disabled, never show the permissions return showDangerous && DangerousPermissionHandlingMode.getCurrent() != DangerousPermissionHandlingMode.DISABLED; } return showPermission(p); } else if (type.equals(PROJECT)) { return p == Item.CREATE && isCreateAllowed() && p.getEnabled() || p != Item.CREATE && p.getEnabled(); } else if (type.equals(SLAVE)) { return p!=Computer.CREATE && p.getEnabled(); } else { return false; } } } }